package com.mitou.common.interceptor;

import com.alibaba.fastjson.JSON;
import com.mitou.base.entity.BaseUser;
import com.mitou.base.service.IBaseMenuService;
import com.mitou.common.annotation.OpenAuth;
import com.mitou.common.annotation.RoleAuth;
import com.mitou.common.constants.BaseConstants;
import com.mitou.common.response.Result;
import com.mitou.common.response.ResultCode;
import com.mitou.common.utils.web.BaseContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * <p>
 * 权限校验拦截
 * <p/>
 *
 * @author rice
 * @since 2021-03-26
 */
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {

    @Resource
    private IBaseMenuService baseMenuService;

    @Override
    public boolean preHandle(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            Method method = ((HandlerMethod) handler).getMethod();
            //1.检查开放注解，如果有，则直接放行。
            //获取注解
            OpenAuth openAuth = method.getAnnotation(OpenAuth.class);
            //如果方法上没有注解，则获取类上的注解
            if (null == openAuth) {
                openAuth = method.getDeclaringClass().getAnnotation(OpenAuth.class);
            }
            //如果有注解则放行
            if (null != openAuth) {
                return true;
            }
            if (null != request) {
                BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
                //根据token获取用户信息
                BaseContextUtil baseContextUtil = factory.getBean(BaseContextUtil.class);
                String token = request.getHeader(BaseConstants.TOKEN_MAME);
                BaseUser loginUser = baseContextUtil.getLoginUser(token);
                //bean有未注入的风险
                if (null == baseMenuService) {
                    baseMenuService = factory.getBean(IBaseMenuService.class);
                }
                //验证权限
                if (this.hasPermission(method, loginUser)) {
                    //验证通过后为token续期
                    baseContextUtil.renewalToken(token);
                    return true;
                }
            }
        }
        //如果没有权限，则返回403异常，数据可返回已经定义好的状态码。
        if (null != response) {
            response.setStatus(HttpStatus.FORBIDDEN.value());
            response.setHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
            response.getOutputStream().write(JSON.toJSONBytes(Result.fail(ResultCode.PERMISSION_NO_ACCESS)));
        }
        return false;
    }

    /**
     * 鉴权
     *
     * @param method
     * @param loginUser
     * @return
     */
    private boolean hasPermission(Method method, BaseUser loginUser) {
        //2.检查权限校验注解
        //获取注解
        RoleAuth roleAuth = method.getAnnotation(RoleAuth.class);
        //如果方法上没有注解，则获取类上的注解
        if (null == roleAuth) {
            roleAuth = method.getDeclaringClass().getAnnotation(RoleAuth.class);
        }
        //仍没有注解则放行
        if (null == roleAuth) {
            return true;
        }
        //如果标记了注解，则校验权限
        if (StringUtils.isNotBlank(roleAuth.value())) {
            //获取该用户的权限信息进行鉴权
            Set<String> permissionSet = loginUser.getPermissionSet();
            //如果权限清单为空，则直接返回
            if (CollectionUtils.isEmpty(permissionSet)) {
                return false;
            }
            return permissionSet.contains(roleAuth.value());
        }
        return true;
    }

}
